﻿using System;
using System.Diagnostics;
using ParboiledSharp.parboiled.dataobjects;
using ParboiledSharp.parboiled.rules;
using ParboiledSharp.parboiled;
using ParboiledSharp.lang.roboline.grammar;
using ParboiledSharp.lang.roboline.interpreter;

namespace ParboiledSharp
{
    class Programm
    {
        class Result
        {
            public int passed = 0;
            public int failedGrammar = 0;
            public int failedResult = 0;


            public static Result operator +(Result rls, Result rhs)
            {
                Result r = new Result();
                r.failedGrammar = rls.failedGrammar + rhs.failedGrammar;
                r.failedResult = rls.failedResult + rhs.failedResult;
                r.passed = rls.passed + rhs.passed;
                return r;
            }
        };
        #region COLORCONSOLE
        [System.Runtime.InteropServices.DllImport("kernel32.dll")]
        public static extern bool SetConsoleTextAttribute(IntPtr hConsoleOutput,
          int wAttributes);
        [System.Runtime.InteropServices.DllImport("kernel32.dll")]
        public static extern IntPtr GetStdHandle(uint nStdHandle);
        static uint STD_OUTPUT_HANDLE = 0xfffffff5;

        private static void setColor(int color)
        {
            IntPtr hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
            SetConsoleTextAttribute(hConsole, color);
        }
        #endregion

        static void Main()
        {
            var test = new RoboLineGrammar();
            Context context = new Context();
            Result result = new Result();
            ParboiledSharp.parboiled.rules.Action.DisableAllActions();
            result += testGrammarExpressions(test.TestExpression(), context);
            result += testGrammarAssignments(test.TestAssignemnt(), context);
            result += testGrammarBlock(test.TestBlock(), context);
            ParboiledSharp.parboiled.rules.Action.EnableAllActions();
            result += testInterpreterExpressions(test.TestExpression(), context);

            
            setColor(7);
            Console.Out.WriteLine(String.Format("TOTAL GRAMMAR TESTS COUNT:{0}", result.passed + result.failedGrammar+result.failedResult));
            Console.Out.WriteLine(String.Format("TESTS PASSED:{0}", result.passed));
            Console.Out.WriteLine(String.Format("TESTS GRAMMAR FAILED:{0}", result.failedGrammar));
            Console.Out.WriteLine(String.Format("TESTS EXECUTE FAILED:{0}", result.failedResult));
            Console.ReadKey();
        }

        private static Result testGrammarExpressions(Rule rule, Context context)
        {
            object[,] tests = {
                              {" \"asfa\\nfadf\"",             true},      
                              {" x ",                          true}, 
                              {" 55 ",                         true}, 
                              {" 55.65 ",                      true}, 
                              {" 55.65==4 and 45<d ",          true},
                              {"((55.65==4) and ( 45 < d ) ) ",true},
                              {" r>3 or 4==4 ",                true},
                              {" (55.65==4) and ( 45 < d ) ) ",false},
                              {"34 + \"asfafadf\"",            true},
                              {"\"hello\"+\"world\" / 45",     true},
                              {"\"hello\" \"world\" / 45",     false},
                              {" ( ( x +  2  ) * 2 ) - 3 / 2", true},
                              {" f( 45 ) ",                    true},
                              {"( ( 2 +  x  )df * 2 ) - 3 / 2",false},
                              {"56+tre(34 , y,3)",             true},
                              {"someFunct(34 , 56,3,)",        false},
                              {"nested(red(3,6)+4 ,34)",       true},
                              {"nested(red(3,6+)+4 ,34)",      false},
                              {"x+6",                          true},
                              {"x.t+6",                        true},
                              {"x.t()+6",                      true},
                              {"x[\"erre\"]+98",               true},
                              {"x[5]+98",                      true},
                              {"x.[5]+98",                     false},
                              {"x[i+5]+98",                    true},
                              {"x. +6",                        false},
                              {"x + y",                        true},
                              {" - x",                         true},
                              {" + x",                         false},
                              {" x-",                          false},
                              {"* x",                          false},
                              {"x >8",                         true},
                              {"x < (sd3+4)",                  true},
                              {"(34+sd) <= 4",                 true},
                              {"(34+sd) >= 4",                 true},
                              {"(34+sd) == 4",                 true},
                              {"(34+sd) => 4",                 false},
                              {" (34 + sd) != 4",              true},
                              {" (34 + sd) =! 4",              false},
                              {"(34+sd) !=! 4",                false},
                              {"!(34+sd) != 4",                false},
                              {"(34+sd) -!= 4",                false},
                              {" (34 + sd ) = 4 ",             false},
                              {" (34 + s d ) == 4 ",           false},
                              {" {red:34} ",                   true},
                              {" {red 34} ",                   false},
                              {" {red:\"34\", some:34} ",      true},
                              {" {red:\"34\"  some:34} ",      false},
                              {" {method:'(x){return 6;}} ",   true},
                              {" {method:{nested_a:34+5, \nnested_b:\"some text\"}} ",   true},
                              {" {method:{nested_a:34+5, nested_b}",false},
                             };
            return testGrammar(rule, context, tests);
        }

        private static Result testGrammarAssignments(Rule rule, Context context)
        {
            object[,] tests = {
                              {" x = 5 ",                       true},
                              {" asf_34=65+98 ",                true},
                              {"asdlk__$adf=34 ",               true},
                              {"\t\tasdlk__adf\t=34+\t\n8 \t",  true},
                              {"asdlk__adf=f(34)+t",            true},
                              {"%x=t=8",                        false},
                              {"%x = 5 ",                       true},
                              {"global asf_34=65+98 ",          true},
                              {"asdlk__$adf=34",                true},
                              {"%asdlk__adf=34",                true},
                              {"%asdlk__adf=f(34)+t",           true},
                              {"%d asdlk__adf=f(34)+t",         false},
                              {"%asdlk__adf =\"adf\"",          true},
                              {"%asdlk__adf= \"\"",             true},
                              {"%asdlk__adf= \"\"==56",         true},
                              {"%asdlk__adf= (\"\"==56)",       true},
                             };
            return testGrammar(rule, context, tests);
        }

        private static Result testGrammarBlock(Rule rule, Context context)
        {
            object[,] tests = {
                               {@"
%main='{
%a=5;
%b=9;
%c=4;
a.print='(toPrint){
                 printValue(toPrint);
             };
if(a<b){
    c.value=""somevalue"";
}else{
    c.value=""asdasdas"";
}
a.print(c.value);
%v={prop_a:v, prop_b:""we"", method:'{return this.prop_a+98;}};
};
main();
", true},
                              {@" f(34); 
                                  34+65; 
                                  %x=4;",                                   true},
                              {@"f(34)
                                 34+65;
                                 %x=4;",                                    false},
                              {@"if(1){
                                 %x=45;
                                 x=x+8;
                                 }",                                        true},
                              {@"if(){
                                 %x=45;
                                 x=x+8;
                                 }",                                        false},
                              {@"if(true){}",                               true},
                              {@"IF(TRUE){}ELSE{r;}",                       true},
                              {@"If(true){}else{r;}else{e;}",               false},
                              {@"if(true){98;}else if(false){r;}else{e;}",  true},
                              {@"if{98;}",                                  false},
                              {@"if(true){sdasd}",                          false},

                              {@" цикл(%i=0;i<7;i=i+1){
                                    %x=3;
                                  }",                                       true},

                              {@" for(i=0 i<7;i=i+1){
                                    % x=3;
                                  }",                                       false},
                               {@" for(i=0; i<7;i+1){
                                    %x=3;
                                  }",                                       true},
                               {@" for(i=0,b=4; i<7;i+1){
                                    %x=3;
                                  }",                                       true},
                               {@" x=3; ",                                  true},
                               {@" for(; i<7;i+1){
                                    %x=3;
                                  }",                                       true},
                               {@" for(   ; ;   ){
                                    %x=3;
                                  }",                                       true},
                               {@" for(   ; ; ;  ){
                                    %x=3;
                                  }",                                       false},
                               {"for(;;){};",                               true},
                               {"for(%x='{this.x++;return this.x;},x=0;x()<45;){};",true},
                               {"for(i=6;i<40;i++){};",                     true},
                               {"for(i=ct*6;(i<40 and x>6);i--){};",        true},
                               {"for(i=6;i<40;i**){};",                     false},
                               {"'{%x=45;};",                               true},
                               {"локальная function='{%x=45;};",            true},
                               {"%function '{%x=45;};",                     false},
                               {"глобальная функция= ' {%ч=45; вернуть ч;} ;",true},
                               {"%function=' {%х=45;}",                     false},
                               {"local function='{%ч=45;return x;};",       true},
                               {"%function=' [r,x] {%x=45;return x;};",     true},
                               {"%function='(x,h)[r,x]{%x=45;return x;};function(x,6);", true},
                               {"%function='(y,x)[4,x]{%x=45;return x;};",  false},
                               {"%function='(x,y)[x,] {%x=45;return x;};",  false},
                               {"%function='(y,u)[x,4]{%x=45;return x;};",  false},
                               {"%function='(3,u)[x,u]{%x=45;return x;};",  false},
                               {"global c=;",                               false},
                               {"global c;",                                true},
                               {"global c=34;",                             true},
                               {"%function='(g,u){%x=45;return x;};",       true},
                               {"return 56;",                               true},
                               {"return true;",                             true},
                               {"sdreturn 56;",                             false},
                               {"%x='(z){return z;}(45);",                  true},
                               {"x++;",                                     true},
                               {"y.r++;",                                   true},
                               {"y.r--;",                                   true},
                               {"45--;",                                    false},
                             };
            return testGrammar(rule, context, tests);
        }

        private static Result testGrammar(Rule rule, Context context, object[,] tests)
        {
            int failed = 0;
            int passed = 0;

            for (int i = 0; i < tests.GetLength(0); i++)
            {
                String testData = (string)tests[i, 0];
                context.SetStream(testData, false);
                bool res = rule.Execute(context);
                bool expected = (bool)tests[i, 1];
                if (res == expected)
                {
                    passed++;
                }
                else
                {
                    setColor(12);
                    Console.Out.WriteLine("FAILED: " + testData);
                    failed++;
                }
            }

            Result r = new Result();
            r.passed = passed;
            r.failedGrammar = failed;
            return r;
        }


        private static Result testInterpreterExpressions(Rule rule, Context context)
        {
            object[,] tests = {
                                  {"(1+2) * (3+4)",true},
                              };
            return testInterpreterExpression(rule, context, tests);

        }

        private static Result testInterpreterExpression(Rule rule, Context context, object[,] tests)
        {
            int failedGrammar = 0;
            int failedInterpretion = 0;
            int passed = 0;

            for (int i = 0; i < tests.GetLength(0); i++)
            {
                String testData = (string)tests[i, 0];
                object expectedValue = (object)tests[i, 1];
                context.SetStream(testData, true);
                bool res = rule.Execute(context);
                if (res == false)
                {
                    setColor(12);
                    Console.Out.WriteLine("FAILED GRAMMAR: " + testData);
                    failedGrammar++;
                }
                else
                {
                    CommandsStorage commands = CommandsStorage.FromParserStack(context.GetStack());
                    object result = context.GetStack().Pop();
                    if (result == expectedValue)
                    {
                        passed++;
                    }
                    else
                    {
                        setColor(12);
                        Console.Out.WriteLine("FAILED EXEC: " + testData);
                        failedInterpretion++;
                    }
                }
            }

            Result r = new Result();
            r.passed = passed;
            r.failedGrammar = failedGrammar;
            r.failedResult = failedInterpretion;
            return r;
        }
    }
}
